#include "fixed_size_allocator.hpp"
#include "tools.hpp"

FixedSizeAllocator::FixedSizeAllocator(int block_size, int block_num)
	: _head(-1),
	  _free_count(0),
	  _block_size(block_size),
	  _block_num(block_num),
	  _base(0)
{
	assert(block_size > 0 && block_num > 0);

	assert((_base = (char*)malloc(_block_size * _block_num)) != NULL);

	for (int i = 0; i < (_block_num - 1); ++ i)
	{
		*((int*)(get_addr_by_index(i))) = (i + 1);
	}

	*((int*)(get_addr_by_index(_block_num - 1))) = -1;
	
	_head = 0;
	_free_count = _block_num;
	_end = _base + _block_size * (_block_num + 1);
}

FixedSizeAllocator::~FixedSizeAllocator()
{
	::free(_base);
	_base = 0;
	_head = -1;
	_free_count = 0;
	_end = 0;
}

void* FixedSizeAllocator::allocate()
{
	if (_head == -1)
	{
		return NULL;
	}

	void * addr = get_addr_by_index(_head);
	_head = *((int*)(addr));
	-- _free_count;
	
	return addr;
}

void FixedSizeAllocator::deallocate(void * addr)
{
	int index = get_index_by_addr(addr);
	*((int*)(addr)) = _head;
	_head = index;
	++ _free_count;
}

void * FixedSizeAllocator::get_addr_by_index(int index)
{
	assert(index >= 0 && index < _block_num);

	return (void*)(_base + (index * _block_size));
}

int FixedSizeAllocator::get_index_by_addr(void * addr)
{
	assert(addr >= _base && addr < _end);
	assert((((char*)addr - _base) % _block_size) == 0);

	return (((char*)addr - _base) / _block_size);
}

void FixedSizeAllocator::display()
{
	for (int head = _head; head != -1;)
	{
		std::cout << head << "\t";
		head = *((int*)get_addr_by_index(head));
	}
	std::cout << std::endl;
}

